import random
from manim import *
from manim.utils.rate_functions import smooth
import numpy as np
from numpy import linalg


class Blob(Circle):
    def __init__(
            self,
            stroke_color=TEAL,
            fill_color=BLUE_E,
            fill_opacity=1,
            random_seed=0,
            random_nudge_size=0.5,
            height=2,
            **kwargs
    ):
        super().__init__(
            stroke_color=stroke_color,
            fill_color=fill_color,
            fill_opacity=fill_opacity,
            **kwargs
        )

        random.seed(random_seed)
        self.random_seed = random_seed
        self.random_nudge_size = random_nudge_size
        self.height = height

        # Apply random deformation
        self.apply_complex_function(
            lambda z: z * (1 + self.random_nudge_size * (random.random() - 0.5))
        )
        self.set_height(self.height)

    def probably_contains(self, point):
        border_points = np.array([p for p in self.get_anchors()])
        distances = [linalg.norm(p - point) for p in border_points]
        min3 = border_points[np.argsort(distances)[:3]]
        center_direction = self.get_center() - point
        in_center_direction = [
            np.dot(p - point, center_direction) > 0 for p in min3
        ]
        return sum(in_center_direction) <= 2


class RightHand(VMobject):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        hand = SVGMobject("RighHandOutline")
        self.inlines = VMobject(*hand.submobjects[:-4])
        self.outlines = VMobject(*hand.submobjects[-4:])
        self.inlines.set_stroke(color=GREY_D, width=3)
        self.outlines.set_stroke(color=WHITE, width=5)
        self.add(self.inlines, self.outlines)
        self.move_to(point_or_mobject=ORIGIN).set_height(2)


class OpeningQuote(Scene):
    def construct(self):
        words = Tex(
            "``The purpose of computation is \\\\",
            "insight",
            ", not ",
            "numbers.",
            "''",
        )
        words.to_edge(edge=UP)
        words[1].set_color(color=BLUE)
        words[3].set_color(color=GREEN)
        author = Tex("-Richard Hamming")
        author.set_color(color=YELLOW)
        author.next_to(mobject_or_point=words, direction=DOWN, buff=0.5)

        self.play(FadeIn(words))
        self.wait(duration=2)
        self.play(Write(vmobject=author, run_time=3))
        self.wait()


class MovingForward(Scene):
    def construct(self):
        bubble = Circle(radius=1.5, color=GREEN, fill_opacity=0.3)
        bubble.to_edge(edge=UP).shift(RIGHT * 2)

        teacher_text = Text(text="Y'all know about linear\ntransformations, right?")
        teacher_text.width = 5
        teacher_text.next_to(mobject_or_point=bubble, direction=DOWN)

        student_text = Text(text="Hmm...")
        student_text.set_color(color=BLUE)
        student_text.next_to(mobject_or_point=bubble, direction=UP)

        self.play(
            Create(mobject=bubble),
            Write(vmobject=teacher_text)
        )
        self.play(
            Write(vmobject=student_text),
        )
        self.wait(duration=2)


class StretchingTransformation(LinearTransformationScene):
    def __init__(self):
        super().__init__()

    def construct(self):
        self.setup()
        self.add_title(title="Generally stretches space")
        matrix = [[0, 1], [-1, 0]]
        self.apply_matrix(matrix=matrix)
        self.wait()


class SquishingTransformation(LinearTransformationScene):
    def __init__(self):
        super().__init__()
        self.plane_config = {
            "x_range": [-10, 10, 1],
            "y_range": [-10, 10, 1],
            "faded_line_ratio": 0
        }

    def construct(self):
        self.setup()
        self.add_title(title="Generally squishes space")
        matrix = [
            [0.5, -0.5],
            [1, 1.0 / 3]
        ]
        self.apply_matrix(matrix=matrix)
        self.wait()


class AskAboutStretching(LinearTransformationScene):
    def construct(self):
        self.setup()
        words = Tex(
            "Exactly how much are \\\\",
            "things being stretched?",
        )
        words.set_color(color=YELLOW)
        words.to_corner(corner=UP + RIGHT)
        words.add_background_rectangle()

        matrix = [
            [2, 1],
            [-1, 3]
        ]
        self.apply_matrix(matrix=matrix, added_anims=[Write(words)])
        self.wait()


class AskAboutStretchingSpecifically(LinearTransformationScene):
    def construct(self):
        self.setup()
        title = VGroup(
            Text("How much are"),
            Text("areas").set_color(YELLOW),
            Text("scaled?")
        )
        title.arrange(direction=RIGHT)
        title.to_edge(edge=UP)
        self.add(title)

        blob = Blob().shift(UP + RIGHT)
        label = Text("Area").set_color(YELLOW)
        label.move_to(blob)

        target_label = MathTex("c \\cdot", "\\text{Area}")
        target_label[1].set_color(YELLOW)

        self.add_transformable_mobject(blob)
        self.add_moving_mobject(label, target_label)
        self.wait()

        matrix = [
            [2, -1],
            [1, 1]
        ]
        self.apply_matrix(matrix)

        arrow = Arrow(title[2].get_bottom(), label.get_top())
        self.play(Create(arrow))
        self.wait()

class BeautyNowUsesLater(Scene):
    def construct(self):
        text = Text("Beauty now uses later")
        text.scale(1.5)
        self.play(Write(text))
        self.wait(2)

class DiagonalExample(LinearTransformationScene):
    def __init__(self, show_square=False, show_coordinates=True, transposed_matrix=[[3,0],[0,2]]):
        super().__init__()
        self.show_square = show_square
        self.show_coordinates = show_coordinates
        self.transposed_matrix = transposed_matrix

    def construct(self):
        self.setup()
        matrix_obj = Matrix(np.array(self.transposed_matrix).T)
        matrix_obj.set_column_colors(X_COLOR, Y_COLOR)
        matrix_obj.to_corner(UL)
        matrix_background =  BackgroundRectangle(matrix_obj)

        self.play(Create(matrix_background), Write(matrix_obj))

        if self.show_square:
            self.add_unit_square(animate=True)

        self.add_foreground_mobjects(matrix_background, matrix_obj)
        self.wait()

        # apply transformations step by step
        self.apply_matrix([self.transposed_matrix[0], [0,1]])
        self.apply_matrix([[1,0], self.transposed_matrix[1]])
        self.wait()

        if self.show_square:
            bottom_brace = Brace(self.i_hat, DOWN)
            right_brace = Brace(self.square, RIGHT)
            width = Tex(str(self.transposed_matrix[0][0]))
            height = Tex(str(self.transposed_matrix[1][1]))
            width.next_to(bottom_brace, DOWN)
            height.next_to(right_brace, RIGHT)

            for mob in [bottom_brace, width, right_brace, height]:
                mob.add_background_rectangle()
                self.play(Write(mob, run_time=0.5))
            self.wait()

            width_target = width.copy()
            height_target = height.copy()
            det = np.linalg.det(self.transposed_matrix)
            times = Tex("\\times")
            eq_det = Tex(f"= {det}")
            words = Text("New area = ")

            equation = VGroup(words, width_target, times, height_target, eq_det)
            equation.arrange(RIGHT, buff=0.2)
            equation.next_to(self.square, UP, aligned_edge=LEFT)
            equation.shift(0.5*RIGHT)
            background_rect = BackgroundRectangle(equation)

            self.play(
                Create(background_rect),
                Transform(width.copy(), width_target),
                Transform(height.copy(), height_target),
                *[Write(mob) for mob in [words, times, eq_det]]
                    )
            self.wait()



if __name__ == '__main__':
    from manim._config import config

    config.preview = True
    config.quality = "low_quality"

    scene = AskAboutStretchingSpecifically()
    scene.render()
